-- Utilities variables
local storyboard = require( 'storyboard' )
local util = require( 'utils' )
local speedupitem = require( 'speedUpItem' )

local _centerX = display.contentCenterX
local _centerY = display.contentCenterY
local _W = display.contentWidth
local _H = display.contentHeight

local physics = require( 'physics' )
physics.start()
physics.setGravity( 0, 10 )

-- seed random
util:seed()

-- Declares game objects
local gameView
local uiView
local gameBg

local player
local blocks = {}
local items = {}

local nLive
local uiLive

local nScore = 0
local uiScore

local nSpeedUp = -40
local nJumpUp = -25

local nBlocks = 0
local nItems = 0
local realNItem = 0

local _FIRST_NEAR_BOTTOM = _H - 20
local deltaCamera = 0

local isCollide = false
local isItemCollected = false
local lastTime = 0
local lastItem = 0

-- timer handle create game object
local itemTimer

local scene = storyboard.newScene()
scene:addEventListener( "createScene", scene )
scene:addEventListener( "destroyScene", scene )

------------------ function contents----------------

----------------------------------------------------
-- scene handle
function scene:createScene( event )
	self:init()
end

-- roll to lock screen if game scene have been ended
function scene:destroyScene( event )
	storyboard.gotoScene( "lockscreen" )
end

function scene:init()

	-- Init default game 
	nLive = 3 -- need to save and load from data (save to facebook or file)
	nScore = 0

	-- loading game background
	gameBg = display.newImage( 'bg.png' )
	gameBg:translate( _centerX, _centerY )

	-- init game layers
	gameView = display.newGroup()
	uiView = display.newGroup()

	-- init game info-bar
	uiLive = display.newText( uiView, 'x'..nLive, 290, 22, system.nativeFont, 15 )
	uiScore = display.newText( uiView, '0', 10, 22, system.nativeFont, 15 )

	-- create game player
	self:createPlayer()
	-- create first default block on screen
	self:createBlocks()
	scrollNBlock( 10 ) -- init bonus 10 block

	self:startGame()
end

-- create default game player
function scene:createPlayer()

	-- try remove last player
	if( player ~= nil ) then
		player:removeSelf( )
		player = nil
	end

	player = display.newImage( gameView, 'roll.png' )
	player:translate( _centerX, _centerY )
	player.velocityX = 0
	player.velocityY = 0
	player.gravity = 10
end

function createBlock()
	return display.newImage( gameView, 'normal-block.png' )
end

-- create current default block on screen at begin of game
function scene:createBlocks()
	local _nBlock = 10
	for i = _nBlock, 1, -1 do 

		nBlocks = nBlocks + 1
		blocks[i] = createBlock()

		blocks[i].x = math.random( blocks[i].contentWidth/2, _W - blocks[i].contentWidth/2 )
		blocks[i].y = i * math.random( 40, 70 )
	end
end

-----------------------------------------------------

-- just create speed up item at the last block
function createSpeedUpItem()
	if( lastItem == nBlocks or realNItem >= 2 ) then
		return
	end

	local sui = speedupitem:newItem( graphics, display, gameView, nil )
	sui:move( atX, atY )
	sui:setNSpeedUp( nSpeedUp )
	sui:stand()

	-- get the last block (lb)
	local lb = blocks[nBlocks]
	if( lb ~= nil ) then
		sui:move( lb.x, util:getTop( lb ) - sui:getHeight()/2 )
	end
	lastItem = nBlocks

	nItems = nItems + 1
	realNItem = realNItem + 1
	items[nItems] = sui

	return sui
end

function nextPosition()
	if( blocks[nBlocks] == nil ) then
		return nil
	end
	return blocks[nBlocks].y - math.random( 30, 50 )
end

function calcBlockNeed()
	return math.floor( (math.abs( gameView.y - _FIRST_NEAR_BOTTOM ) + 100) * 1 / 10 ) - nBlocks
end

-- add block on scroll
function scrollNBlock( n )
	if n <= 0 then return end

	local lastBlock = nBlocks
	for i = 1, n do 
		lastBlock = nBlocks + 1
		blocks[lastBlock] = createBlock()
		blocks[lastBlock].x = math.random( blocks[lastBlock].contentWidth/2, _W - blocks[lastBlock].contentWidth/2 )

		-- keep random high from the last block
		blocks[lastBlock].y = nextPosition()
		
		-- update last block to the current
		nBlocks = lastBlock
	end
end

function scrollBlocks()
	scrollNBlock( calcBlockNeed() )
end

-----------------------------------------------------
-- Game flow
function scene:startGame()
	Runtime:addEventListener( 'enterFrame', gameLoop )
	Runtime:addEventListener( 'touch', onTouch )
	Runtime:addEventListener ("accelerometer", onAccelerate);

	itemTimer = timer.performWithDelay(800, createSpeedUpItem, 0)
	end

function scene:endGame()
	Runtime:removeEventListener( 'enterFrame', gameLoop )
	Runtime:removeEventListener( 'touch', onTouch )
	Runtime:removeEventListener("accelerometer", onAccelerate);

	timer.cancel( itemTimer )
end

-----------------------------------------------------
-- Utils
function isPlayerInRangeX( block )
	return not ( util:getLeft( player ) > util:getRight( block ) or 
		util:getRight( player ) < util:getLeft( block ) )
end

function isPlayerInRangeY( velocity, block )
	local bot = util:getBottom( player )
	return ( bot > util:getTop( block ) and bot < util:getBottom( block ) )
end

function isPlayerCollideItem( sui )
	return not ( util:getLeft( player ) > sui:getRight() or 
		util:getRight( player ) < sui:getLeft() or
		util:getTop( player ) > sui:getBottom() or 
		util:getBottom( player ) < sui:getTop() )
end

function getBottomLimitation()
	return _H - gameView.y - 20
end

function getVirtualBottom()
	return _H - gameView.y
end

----------------------------------------------------
-- Game logic
-- Handle collision with blocks, accept collision with the highest block, reject the other ones
function handlePlayerCollision( velocity )

	if( player == nil ) then
		return 
	end

	local highestBlock = nil

	for i in pairs(blocks) do
		if( velocity > 0 and isPlayerInRangeX( blocks[i] ) and isPlayerInRangeY( velocity, blocks[i] ) ) then
			player.velocityY = nJumpUp
			isCollide = true
			if( highestBlock == nil ) then
				highestBlock = blocks[i]
			elseif( highestBlock.y < blocks[i].y ) then
				highestBlock = blocks[i]
			end
		end
	end

	if( highestBlock ~= nil ) then
		return highestBlock.y
	end
	return nil
end

function handlePlayerCollisionWithItem()
	if( player == nil ) then
		return 
	end

	for i in pairs( items ) do
		if isPlayerCollideItem( items[i] ) then
			items[i]:affect( player )
			isItemCollected = true
		end
	end
end

function removeBlock( block, index )
	if( block ~= nil ) then
		block:removeSelf()
		blocks[index] = nil
	end
end

function removeItem( item, index )
	if( item ~= nil ) then
		item:removeSelf()
		items[index] = nil
		
		realNItem = realNItem - 1
	end
end

function removeOutScreenBlocks()
	for i in pairs(blocks) do
		if( blocks[i] ~= nil and util:getTop( blocks[i] ) > getBottomLimitation() ) then
			timer.performWithDelay(500, removeBlock(blocks[i], i) )
		end
	end
end

function removeoutScreenItems()
	for i in pairs(items) do
		if( items[i] ~= nil and items[i]:getTop() > getBottomLimitation() ) then
			timer.performWithDelay(500, removeItem(items[i], i) )
		end
	end
end

function handleOutScreenPlayer()
	if( util:getLeft( player ) > _W ) then
		player.x = 0
	elseif( util:getRight( player ) < 0 ) then
		player.x = _W
	end

	-- check player die here !!
	if( util:getTop( player ) > (_H - gameView.y) ) then
		print( 'dead' )
		player.y = -gameView.y + _centerY
	end
end

----------------------------------------
-- move camera along with player position
function moveCamera( distance, t )
	if( distance ~= 0 ) then
		transition.to( gameView, { y= math.floor(gameView.y +distance), time=t } )
	end
end

function checkCreateBlocks( distance )
	deltaCamera	= distance
end

function handleImmediateCamera( distance, delta )
	if( distance > 0 ) then
		isItemCollected = false
		return
	end

	d = math.floor( math.abs( distance ) )
	gameView:translate( 0, d )

	checkCreateBlocks( d )
end

function handleCamera( collisionPosition )

	if( collisionPosition == nil or not isCollide ) then
		return
	end

	local d = 0
	local nb = getBottomLimitation()
	if( collisionPosition < nb ) then
		d = math.floor( math.abs( collisionPosition - nb ) )
	end

	checkCreateBlocks( d )
	moveCamera( d, 700 )
end

----------------------------------------
function resetCollisionState()
	isCollide = false
end

-- update all game actions
function gameLoop( event )

	local deltaTime = event.time/1000 - lastTime
	lastTime = lastTime + deltaTime

	resetCollisionState()

	local velocity = player.gravity + player.velocityY
	if( player ) then 
		player:translate( player.velocityX, velocity )
	end

	-- reduce force by one
	player.velocityY = player.velocityY + 3*deltaTime*player.gravity
	if( player.velocityY >= 0 ) then
		player.velocityY = 0
	end

	-- handle game collision
	local collisionPosition = handlePlayerCollision( velocity )
	
	if isItemCollected then
		handleImmediateCamera( velocity )
	else
		handleCamera( collisionPosition )
	end

	handlePlayerCollisionWithItem()

	removeOutScreenBlocks()
	removeoutScreenItems()

	handleOutScreenPlayer()

	scrollBlocks()
end

--------------------------------------------------
-- user's acceleration
function onAccelerate( event )
	player.x = player.x + (player.x * event.xGravity)
end

--------------------------------------------------
-- user touch on screen
local _isFocus = false
local _lastPos = { x = 0; y = 0 }
function onTouch( event )
	if event.phase == "began" then
		_isFocus = true
		_lastPos.x = event.x
		_lastPos.y = event.y
	elseif _isFocus then
		if event.phase == "moved" then
			player:translate( event.x - _lastPos.x, 0 )

			_lastPos.x = event.x
			_lastPos.y = event.y
			elseif event.phase == "ended" or event.phase == "cancelled" then
			_isFocus = false
		end
	end
	return true
end 

return scene